#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

enum EXITCODE
{
    USAGE_ERR,
    SOCKET_ERR
};

void Usage(string process)
{
    cerr << "Usage: " << process << " server_ip server_port" << endl;
}

enum class CONSTAT
{
    NEW,
    CONNECTED,
    CONNECTING,
    DISCONNECTED,
    CLOSE
};

const int default_fd = -1;
const int default_retry_interval = 1;
const int default_max_num = 5;

class Connection
{
public:
    Connection(const string ip, const uint16_t port)
        : _sockfd(default_fd),
          _ip(ip),
          _port(port),
          _status(CONSTAT::NEW),
          _retry_interval(default_retry_interval),
          _max_num(default_max_num)
    {
    }

    void Connect()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            cerr << "socket error" << endl;
            exit(SOCKET_ERR);
        }
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        inet_pton(AF_INET, _ip.c_str(), &server.sin_addr);

        int n = connect(_sockfd, (const sockaddr *)&server, sizeof(server));
        if (n < 0)
        {
            // cout << "connect failed" << endl;
            Disconnect();
            _status = CONSTAT::DISCONNECTED;
            return;
        }
        _status = CONSTAT::CONNECTED;
    }

    void Reconnect()
    {
        int cnt = 1;
        while (true)
        {
            _status = CONSTAT::CONNECTING;
            if (cnt > _max_num)
            {
                cout << "重连失败,请检查网络" << endl;
                _status = CONSTAT::CLOSE;
                break;
            }
            Connect();
            if (_status == CONSTAT::CONNECTED)
            {
                cout << "第" << cnt << "次重连成功" << endl;
                break;
            }
            cout << "第" << cnt << "次尝试重连" << endl;
            cnt++;
            sleep(_retry_interval);
        }
    }

    void Disconnect()
    {
        if (_sockfd >= 0)
        {
            close(_sockfd);
            _status = CONSTAT::CLOSE;
            _sockfd = -1;
        }
    }

    void Process()
    {
        while(true)
        {
            string messages = "hello";
            int n = write(_sockfd, messages.c_str(), messages.size());
            if(n > 0)
            {
                char buff[1024];
                int m = read(_sockfd, buff, sizeof(buff)-1);
                if(m > 0)
                {
                    buff[m] = 0;
                    cout << buff << endl;
                }
                else if(m == 0)
                {
                    Disconnect();
                    _status = CONSTAT::DISCONNECTED;
                    break;
                }
                else
                {
                    _status = CONSTAT::CLOSE;
                    break;
                }
            }
            else
            {
                cerr << "write error" << endl;
                _status = CONSTAT::CLOSE;
                break;
            }
            sleep(1);
        } 
    }

    int Scokfd() { return _sockfd; }
    CONSTAT Status() { return _status; }

    ~Connection()
    {
        Disconnect();
    }

private:
    int _sockfd;
    uint16_t _port;
    string _ip;
    CONSTAT _status;
    int _retry_interval;
    int _max_num;
};

class Client
{
public:
    Client(string ip, uint16_t port)
        : con(ip, port)
    {
    }
    ~Client() {}

    void Execute()
    {
        while (1)
        {
            switch (con.Status())
            {
            case CONSTAT::NEW:
                con.Connect();
                break;
            case CONSTAT::DISCONNECTED:
                con.Reconnect();
                break;
            case CONSTAT::CONNECTED:
                con.Process();
                break;
            case CONSTAT::CLOSE:
                con.Disconnect();
                return;
            default:
                break;
            }
        }
    }

private:
    Connection con;
};

int main(int args, char *argv[])
{

    if (args != 3)
    {
        Usage(argv[0]);
        exit(EXITCODE::USAGE_ERR);
    }

    string server_ip = argv[1];
    uint16_t server_port = stoi(argv[2]);

    Client client(server_ip, server_port);
    client.Execute();
    return 0;
}

// #include <iostream>
// #include <string>
// #include <cstring>
// #include <cstdlib>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
// using namespace std;
// void Usage(const std::string &process)
// {
//     std::cout << "Usage: " << process << " server_ip server_port"
//               << std::endl;
// }
// enum class Status // C++11 强类型枚举
// {
//     NEW,          // 新建状态，就是单纯的连接
//     CONNECTING,   // 正在连接，仅仅方便查询 conn 状态
//     CONNECTED,    // 连接或者重连成功
//     DISCONNECTED, // 重连失败
//     CLOSED        // 连接失败，经历重连，无法连接
// };
// class ClientConnection
// {
// public:
//     ClientConnection(uint16_t serverport, const std::string
//                                               &serverip)
//         : _sockfd(-1),
//           _serverport(serverport),
//           _serverip(serverip),
//           _retry_interval(1),
//           _max_retries(5),
//           _status(Status::NEW)
//     {
//     }
//     void Connect()
//     {
//         // 1. 创建 socket
//         _sockfd = socket(AF_INET, SOCK_STREAM, 0);
//         if (_sockfd < 0)
//         {
//             cerr << "socket error" << endl;
//             exit(1);
//         }
//             // 2. connect
//             struct sockaddr_in server;
//         memset(&server, 0, sizeof(server));
//         server.sin_family = AF_INET;
//         server.sin_port = htons(_serverport);
//         // p:process(进程), n(网络) -- 不太准确，但是好记忆
//         inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr);
//         // 1. 字符串 ip->4 字节 IP 2. 网络序列
//         int n = connect(_sockfd, (struct sockaddr *)&server,
//                         sizeof(server)); // 自动进行 bind 哦！
//         if (n < 0)
//         {
//             Disconnect(); 
//             _status = Status::DISCONNECTED; // 没有连接成功
//             return;
//         }
//         _status = Status::CONNECTED; // 连接成功
//     }
//     int SocketFd()
//     {
//         return _sockfd;
//     }
//     void Reconnect()
//     {
//         _status = Status::CONNECTING; // 正在重连
//         int count = 0;
//         while (count < _max_retries)
//         {
//             Connect(); // 重连
//             if (_status == Status::CONNECTED)
//             {
//                 return;
//             }
//             sleep(_retry_interval);
//             count++;
//             std::cout << "重连次数: " << count << ", 最大上限: " << _max_retries << std::endl;
//         }
//         _status = Status::CLOSED; // 重连失败，可以关闭了
//     }
//     void Disconnect()
//     {
//         if (_sockfd != -1)
//         {
//             close(_sockfd);
//             _status = Status::CLOSED;
//             _sockfd = -1;
//         }
//     }
//     Status GetStatus()
//     {
//         return _status;
//     }
//     void Process()
//     {
//         // 简单的 IO 即可
//         while (true)
//         {
//             string inbuffer;
//             getline(cin, inbuffer);
//             if (inbuffer.empty())
//                 continue;
//             ssize_t n = write(_sockfd, inbuffer.c_str(),
//                               inbuffer.size());
//             if (n > 0)
//             {
//                 char buffer[1024];
//                 ssize_t m = read(_sockfd, buffer, sizeof(buffer) - 1);
//                 if (m > 0)
//                 {
//                     buffer[m] = 0;
//                     cout << "echo messsge -> " << buffer << endl;
//                 }
//                 else if (m == 0) // 这里证明 server 端掉线了
//                 {
//                     _status = Status::DISCONNECTED;
//                     break;
//                 }
//                 else
//                 {
//                     std::cout << "read m : " << m << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;
//                     _status = Status::CLOSED;
//                     break;
//                 }
//             }
//             else
//             {
//                 std::cout << "write n : " << n << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;
//                 _status = Status::CLOSED;
//                 break;
//             }
//         }
//     }
//     ~ClientConnection()
//     {
//         Disconnect();
//     }

// private:
//     int _sockfd;
//     uint16_t _serverport;       // server port 端口号
//     std::string _serverip;      // server ip 地址
//     int _retry_interval;        // 重试时间间隔
//     int _max_retries;           // 重试次数
//     Status _status;             // 连接状态
// };
// class TcpClient
// {
// public:
//     TcpClient(uint16_t serverport, const std::string &serverip) : _conn(serverport, serverip)
//     {
//     }
//     void Execute()
//     {
//         while (true)
//         {
//             switch (_conn.GetStatus())
//             {
//             case Status::NEW:
//                 _conn.Connect();
//                 break;
//             case Status::CONNECTED:
//                 std::cout << "连接成功, 开始进行通信." << std::endl;
//                 _conn.Process();
//                 break;
//             case Status::DISCONNECTED:
//                 std::cout << "连接失败或者对方掉线，开始重连." << std::endl;
//                 _conn.Reconnect();
//                 break;
//             case Status::CLOSED:
//                 _conn.Disconnect();
//                 std::cout << "重连失败, 退出." << std::endl;
//                 return; // 退出
//             default:
//                 break;
//             }
//         }
//     }
//     ~TcpClient(){
//     } 
// private: 
//     ClientConnection _conn; // 简单组合起来即可
// };
// // class Tcp
// // ./tcp_client serverip serverport
// int main(int argc, char *argv[])
// {
//     if (argc != 3)
//     {
//         Usage(argv[0]);
//         return 1;
//     }
//     std::string serverip = argv[1];
//     uint16_t serverport = stoi(argv[2]);
//     TcpClient client(serverport, serverip);
//     client.Execute();
//     return 0;
// }
